#include "EntityManager.h"

#include "Entity.h"
#include "../SGD Wrappers/SGD_Geometry.h"

void EntityManager::AddEntity(IEntity* entity, unsigned int bucket)
{
	//Expand the table, if needed
	if (bucket >= entities.size())
		entities.resize(bucket + 1);

	//Append the entity into the specified vector
	entities[bucket].push_back(entity);

	//Hold a reference to keep the entity in memory
	entity->AddRef();
}

void EntityManager::RemoveEntity(IEntity* entity, unsigned int bucket)
{
	//Try to find the entity
	EntityVector& vec = entities[bucket];
	for (unsigned int i = 0; i < vec.size(); i++)
	{
		if (vec[i] == entity)
		{
			vec.erase(vec.begin() + i);
			entity->Release();
			break;
		}
	}
}

void EntityManager::RemoveEntity(IEntity* entity)
{
	for (unsigned int bucket = 0; bucket < entities.size(); bucket++)
	{
		EntityVector& vec = entities[bucket];
		for (unsigned int i = 0; i < vec.size(); i++)
		{
			if (vec[i] == entity)
			{
				vec.erase(vec.begin() + i);
				entity->Release();
				return;
			}
		}
	}
}

void EntityManager::RemoveAll(unsigned int bucket)
{
	iterating = true;
	{
		EntityVector& vec = entities[bucket];
		for (unsigned int i = 0; i < vec.size(); i++)
		{
			vec[i]->Release();
			vec[i] = nullptr;
		}
		vec.clear();
	}
	iterating = false;
}

void EntityManager::RemoveAll()
{
	iterating = true;
	{
		for (unsigned int bucket = 0; bucket < entities.size(); bucket++)
		{
			EntityVector& vec = entities[bucket];
			for (unsigned int i = 0; i < vec.size(); i++)
			{
				vec[i]->Release();
				vec[i] = nullptr;
			}
		}
	}
	iterating = false;
	entities.clear();
}

void EntityManager::UpdateAll(float elapsedTime)
{
	iterating = true;
	{
		for (unsigned int bucket = 0; bucket < entities.size(); bucket++)
		{
			EntityVector& vec = entities[bucket];
			for (unsigned int i = 0; i < vec.size(); i++)
				vec[i]->Update(elapsedTime);
		}
	}
	iterating = false;
}

void EntityManager::RenderAll()
{
	
	iterating = true;
	{
		for (unsigned int bucket = 0; bucket < entities.size(); bucket++)
		{
			EntityVector& vec = entities[bucket];
			for (unsigned int i = 0; i < vec.size(); i++)
				vec[i]->Render();
		}
	}
	iterating = true;
}

void EntityManager::CheckCollisions(unsigned int bucket1, unsigned int bucket2)
{
	if (bucket1 >= entities.size()
		|| bucket2 >= entities.size()
		|| entities[bucket1].size() == 0
		|| entities[bucket2].size() == 0)
		return;

	iterating = true;
	{
		if (bucket1 != bucket2)
		{
			EntityVector* pVec1 = &entities[bucket1];
			EntityVector* pVec2 = &entities[bucket2];

			if (pVec2->size() < pVec1->size())
			{
				EntityVector* temp = pVec1;
				pVec1 = pVec2;
				pVec2 = temp;
			}

			EntityVector& vec1 = *pVec1;
			EntityVector& vec2 = *pVec2;
			unsigned int size = vec2.size();
			for (unsigned int i = 0; i < vec1.size(); i++)
			{
				const Entity* temp1 = dynamic_cast<const Entity*>(vec1[i]);
				if (temp1->GetCollisionFlag() == -1)
					continue;
				for (unsigned int j = 0; j < size; j++)
				{
					const Entity* temp2 = dynamic_cast<const Entity*>(vec2[j]);
					if (temp1 == temp2)
						continue;
					if (temp2->GetCollisionFlag() != -1)
					{
						SGD::Vector distance = temp2->GetRect().ComputeCenter() - temp1->GetRect().ComputeCenter();
						float length = distance.ComputeLength();
						if (length < (temp1->GetRadius() + temp2->GetRadius()))
						{
							vec1[i]->HandleCollision(vec2[j]);
							vec2[j]->HandleCollision(vec1[i]);
						}
					}
				}
			}
		}
		else
		{
			EntityVector& vec = entities[bucket1];
			for (unsigned int i = 0; i < vec.size() - 1; i++)
			{
				const Entity* temp1 = dynamic_cast<const Entity*>(vec[i]);
				if (temp1->GetCollisionFlag() == -1)
					continue;
				for (unsigned int j = i + 1; j < vec.size(); j++)
				{
					const Entity* temp2 = dynamic_cast<const Entity*>(vec[j]);
					if (temp1 == temp2)
						continue;

					if (temp2->GetCollisionFlag() != -1)
					{
						SGD::Vector distance = temp2->GetRect().ComputeCenter() - temp1->GetRect().ComputeCenter();
						float length = distance.ComputeLength();
						if (length < (temp1->GetRadius() + temp2->GetRadius()))
						{
							vec[i]->HandleCollision(vec[j]);
							vec[j]->HandleCollision(vec[i]);
						}
					}
				}
			}
		}
	}
	iterating = false;
}

const IEntity* EntityManager::Save(int bucket, int pos)
{
	return entities[bucket][pos];
}
